/*
 * Copyright (c) 2021 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "geofence_policy.h"

namespace OHOS {
namespace Msdp {
const int32_t TIME_HOUR_TO_SEC = 3600;
const int32_t TIME_MIN_TO_SEC = 60;
const int32_t TIME_TILING_STRATEGY_BEGIN  = 6 * 3600;
const int32_t TIME_TILING_STRATEGY_END  = 6 * 3600 + 5 * 60;

const double DISTANCE_ONE_KILOMETERS = 1 * 1000;
const double DISTANCE_TEN_KILOMETERS = 10 * 1000;
const double DISTANCE_THIRTY_KILOMETERS = 30 * 1000;


void GeofencePolicy::GeofenceCallbackDeathRecipient::OnRemoteDied(const wptr<IRemoteObject>& remote)
{
    if (remote == nullptr) {
        GEOFENCE_HILOGE(GEOFENCE_MODULE_SERVICE, "OnRemoteDied failed, remote is nullptr");
        return;
    }
    GEOFENCE_HILOGD(GEOFENCE_MODULE_SERVICE, "Recv death notice");
}

bool GeofencePolicy::Init()
{
    GEOFENCE_HILOGI(GEOFENCE_MODULE_SERVICE, "GeofencePolicy: Init start");
    if (geofenceCBDeathRecipient_ == nullptr) {
        geofenceCBDeathRecipient_ = new GeofenceCallbackDeathRecipient();
    }
    GEOFENCE_HILOGI(GEOFENCE_MODULE_SERVICE, "GeofencePolicy: Init success");

    timeMinuteEnterAndExit_ = geofenceConfig_->GetTimeMinuteEnterAndExitConf();
    timeMinuteLessOneKm_ = geofenceConfig_->GetTimeMinuteLessOneKmConf();
    timeMinuteOneToTenKm_ = geofenceConfig_->GetTimeMinuteOneToTenKmConf();
    timeMinuteTenToThirtyKm_ = geofenceConfig_->GetTimeMinuteTenToThirtyKmConf();
    timeMinuteMoreThirtyKm_ = geofenceConfig_->GetTimeMinuteMoreThirtyKmConf();
    repeatEntryTimes_ = geofenceConfig_->GetRepeatEntryTimesConf();
    timesEnterRadius_ = geofenceConfig_->GetTimesEnterRadiusConf();
    timesExitRadius_ = geofenceConfig_->GetTimesExitRadiusConf();

    geofenceStateMachine_ = std::make_unique<GeofenceStateMachine>(ms_);
    geofenceTrack_ = std::make_unique<GeofenceTrack>(ms_);
    if (geofenceImplClient_ == nullptr) {
        return false;
    }

    if (!InitInterface()) {
        return false;
    }

    GeofenceLocation::CallbackLocationData callbackLocationData = std::bind(&GeofencePolicy::GetLocationData,
        this, std::placeholders::_1);
    if (!geofenceLocation_->InitLocationDataCallback(geofenceImplClient_, callbackLocationData)) {
        GEOFENCE_HILOGE(GEOFENCE_MODULE_SERVICE, "%{public}s init msdp location data callback fail.", __func__);
        return false;
    }

    return true;
}

void GeofencePolicy::GetLocationData(GeofenceDataUtils::LocationData& locationData)
{
    GEOFENCE_HILOGE(GEOFENCE_MODULE_SERVICE, "enter");
    geofenceTrack_->LocationCellData(locationData.cell);
    GeofenceState(locationData.location);
    GEOFENCE_HILOGE(GEOFENCE_MODULE_SERVICE, "enter");
    return;
}

bool GeofencePolicy::InitInterface()
{
    GEOFENCE_HILOGE(GEOFENCE_MODULE_SERVICE, "enter");
    if (geofenceImplClient_ != nullptr) {
        GEOFENCE_HILOGE(GEOFENCE_MODULE_SERVICE, "Init msdp client impl");
        geofenceImplClient_->InitMsdpImpl();
    }
    GEOFENCE_HILOGI(GEOFENCE_MODULE_SERVICE, "exit");
    return true;
}

int32_t GeofencePolicy::GetTimeNow()
{
    GEOFENCE_HILOGI(GEOFENCE_MODULE_SERVICE, "Enter");
    struct tm curTime = {0};
    bool ret = GetSystemCurrentTime(&curTime);
    if (!ret) {
        GEOFENCE_HILOGE(GEOFENCE_MODULE_SERVICE, "Get system current time failed");
        return -1;
    }

    GEOFENCE_HILOGI(GEOFENCE_MODULE_SERVICE, "curTime tm_hour = %{public}d", curTime.tm_hour);
    GEOFENCE_HILOGI(GEOFENCE_MODULE_SERVICE, "curTime tm_min = %{public}d", curTime.tm_min);
    GEOFENCE_HILOGI(GEOFENCE_MODULE_SERVICE, "curTime tm_sec = %{public}d", curTime.tm_sec);
    GEOFENCE_HILOGI(GEOFENCE_MODULE_SERVICE, "Exit");
    return (curTime.tm_hour*TIME_HOUR_TO_SEC + curTime.tm_min * TIME_MIN_TO_SEC + curTime.tm_sec);
}

int32_t GeofencePolicy::GetTimeNowDay()
{
    GEOFENCE_HILOGI(GEOFENCE_MODULE_SERVICE, "Enter");
    struct tm curTime = {0};
    std::string timeString;
    bool ret = GetSystemCurrentTime(&curTime);
    if (!ret) {
        GEOFENCE_HILOGE(GEOFENCE_MODULE_SERVICE, "Get system current time failed");
    }

    GEOFENCE_HILOGI(GEOFENCE_MODULE_SERVICE, "curTime tm_mday = %{public}d", curTime.tm_mday);
    return curTime.tm_mday;
}

int32_t GeofencePolicy::FoolproofTacticsTime()
{
    GEOFENCE_HILOGI(GEOFENCE_MODULE_SERVICE, "Enter");
    int32_t timeNowSec = GetTimeNow();
    int32_t notifyTimeInterval = timeNowSec - lastNotifyTimeFlag_;
    if (timeNowSec >= foolTacticsTime_) {
        foolTacticsTime_ = 0;
        if (notifyTimeInterval >= timeMinuteEnterAndExit_ * TIME_MIN_TO_SEC) {
            lastNotifyTimeFlag_ = timeNowSec;
        } else {
            GEOFENCE_HILOGI(GEOFENCE_MODULE_SERVICE, "Remaining time is %{public}lf",
                timeMinuteEnterAndExit_ * TIME_MIN_TO_SEC - notifyTimeInterval);
            GEOFENCE_HILOGI(GEOFENCE_MODULE_SERVICE, "Exit not reached the time since the last time notify state");
            return 0;
        }
        GEOFENCE_HILOGI(GEOFENCE_MODULE_SERVICE, "Exit");
        return 1;
    } else {
        GEOFENCE_HILOGI(GEOFENCE_MODULE_SERVICE, "Foolproof strategy remaining time is %{public}d",
            foolTacticsTime_ - timeNowSec);
        GEOFENCE_HILOGI(GEOFENCE_MODULE_SERVICE, "Exit Foolproof strategy is in effect");
        return 0;
    }
}

int32_t GeofencePolicy::FoolproofTacticsDistance(double distance)
{
    GEOFENCE_HILOGI(GEOFENCE_MODULE_SERVICE, "Enter");
    int32_t timeNow = 0;
    timeNow = GetTimeNow();
    if (judgementTimeFlag_ == 0) {
        if (distance <= DISTANCE_ONE_KILOMETERS) {
            lastJudgementTimeFlag_ = timeNow;
            judgementTimeFlag_ = timeMinuteLessOneKm_ * TIME_MIN_TO_SEC;
        } else if (distance >= DISTANCE_ONE_KILOMETERS && distance <= DISTANCE_TEN_KILOMETERS) {
            lastJudgementTimeFlag_ = timeNow;
            judgementTimeFlag_ = timeMinuteOneToTenKm_ * TIME_MIN_TO_SEC;
        } else if (distance >= DISTANCE_TEN_KILOMETERS && distance <= DISTANCE_THIRTY_KILOMETERS) {
            lastJudgementTimeFlag_ = timeNow;
            judgementTimeFlag_ = timeMinuteTenToThirtyKm_ * TIME_MIN_TO_SEC;
        } else {
            lastJudgementTimeFlag_ = timeNow;
            judgementTimeFlag_ = timeMinuteMoreThirtyKm_ * TIME_MIN_TO_SEC;
        }
        GEOFENCE_HILOGI(GEOFENCE_MODULE_SERVICE, "lastJudgementTimeFlag_ = %{public}d", lastJudgementTimeFlag_);
        GEOFENCE_HILOGI(GEOFENCE_MODULE_SERVICE, "judgementTimeFlag_ = %{public}d", judgementTimeFlag_);
        return 1;
    } else {
        if (timeNow < lastJudgementTimeFlag_ + judgementTimeFlag_) {
            GEOFENCE_HILOGI(GEOFENCE_MODULE_SERVICE, "Remaining time is %{public}d",
                lastJudgementTimeFlag_ + judgementTimeFlag_ - timeNow);
            GEOFENCE_HILOGI(GEOFENCE_MODULE_SERVICE,
                "Exit, not reached the time since the last time we judged the distance");
            return 0;
        }
        if (distance <= DISTANCE_ONE_KILOMETERS) {
            lastJudgementTimeFlag_ = timeNow;
            judgementTimeFlag_ = timeMinuteLessOneKm_ * TIME_MIN_TO_SEC;
        } else if (distance >= DISTANCE_ONE_KILOMETERS && distance <= DISTANCE_TEN_KILOMETERS) {
            lastJudgementTimeFlag_ = timeNow;
            judgementTimeFlag_ = timeMinuteOneToTenKm_ * TIME_MIN_TO_SEC;
        } else if (distance >= DISTANCE_TEN_KILOMETERS && distance <= DISTANCE_THIRTY_KILOMETERS) {
            lastJudgementTimeFlag_ = timeNow;
            judgementTimeFlag_ = timeMinuteTenToThirtyKm_ * TIME_MIN_TO_SEC;
        } else {
            lastJudgementTimeFlag_ = timeNow;
            judgementTimeFlag_ = timeMinuteMoreThirtyKm_ * TIME_MIN_TO_SEC;
        }
        GEOFENCE_HILOGI(GEOFENCE_MODULE_SERVICE, "lastJudgementTimeFlag_ = %{public}d", lastJudgementTimeFlag_);
        GEOFENCE_HILOGI(GEOFENCE_MODULE_SERVICE, "judgementTimeFlag_ = %{public}d", judgementTimeFlag_);
        return 1;
    }
}

int32_t GeofencePolicy::GeofenceStateJudgement(std::vector<double> locationData)
{
    GEOFENCE_HILOGI(GEOFENCE_MODULE_SERVICE, "Enter");
    int32_t stateFlag;
    switch (type_) {
        case GeofenceDataUtils::GeofenceType::GEOFENCE_OPTION_ADD_POINT_GEOFENCE: {
            stateFlag = geofenceStateMachine_ -> PointRadiusAlgorithm(pointOptions_.centerCoordinate,
                pointOptions_.radius, locationData);
            break;
        }
        case GeofenceDataUtils::GeofenceType::GEOFENCE_OPTION_ADD_POI_GEOFENCE: {
            if (std::size(cloudData_.fence)!= 0) {
                GEOFENCE_HILOGI(GEOFENCE_MODULE_SERVICE, "fenceDataCache_ = %{public}lf", cloudData_.fence[0][0]);
                stateFlag = geofenceStateMachine_ -> RayCastingAlgorithm(cloudData_.fence, locationData);
            } else {
                GEOFENCE_HILOGI(GEOFENCE_MODULE_SERVICE, "fencedata is not recived");
                return -1;
            }
            break;
        }
        default: {
            GEOFENCE_HILOGI(GEOFENCE_MODULE_SERVICE, "Exit, The status of the fence is not subscribed");
            return -1;
        }
    }
    stateFlag_ = stateFlag;
    return stateFlag;
    GEOFENCE_HILOGI(GEOFENCE_MODULE_SERVICE, "Exit");
}

int32_t GeofencePolicy::GeofenceEnterDistanceJudgement(std::vector<double> locationData)
{
    GEOFENCE_HILOGI(GEOFENCE_MODULE_SERVICE, "Enter");
    double distance;
    if (type_ == GeofenceDataUtils::GeofenceType::GEOFENCE_OPTION_ADD_POINT_GEOFENCE) {
        distance = geofenceStateMachine_->GetDistance(locationData, pointOptions_.centerCoordinate);
        if (distance <= pointOptions_.radius * timesEnterRadius_) {
            GEOFENCE_HILOGI(GEOFENCE_MODULE_SERVICE, "Enter the geofence");
            return 1;
        } else {
            GEOFENCE_HILOGI(GEOFENCE_MODULE_SERVICE, "Not enter the geofence, the distance is not satisfied");
            return 0;
        }
    } else if (type_ == GeofenceDataUtils::GeofenceType::GEOFENCE_OPTION_ADD_POI_GEOFENCE) {
        distance = geofenceStateMachine_->GetDistance(locationData, cloudData_.center);
        if (distance <= cloudData_.radius * timesEnterRadius_) {
            GEOFENCE_HILOGI(GEOFENCE_MODULE_SERVICE, "Exit the geofence");
            return 1;
        } else {
            GEOFENCE_HILOGI(GEOFENCE_MODULE_SERVICE, "Not enter the geofence, the distance is not satisfied");
            return 0;
        }
    } else {
        GEOFENCE_HILOGI(GEOFENCE_MODULE_SERVICE, "Exit, The status of the fence is not subscribed");
        return 0;
    }
    GEOFENCE_HILOGI(GEOFENCE_MODULE_SERVICE, "Exit");
    return 1;
}

int32_t GeofencePolicy::GeofenceExitDistanceJudgement(std::vector<double> locationData)
{
    GEOFENCE_HILOGI(GEOFENCE_MODULE_SERVICE, "Enter");
    double distance;
    if (type_ == GeofenceDataUtils::GeofenceType::GEOFENCE_OPTION_ADD_POINT_GEOFENCE) {
        distance = geofenceStateMachine_->GetDistance(locationData, pointOptions_.centerCoordinate);
        if (distance >= pointOptions_.radius * timesExitRadius_) {
            GEOFENCE_HILOGI(GEOFENCE_MODULE_SERVICE, "Exit the geofence");
            return 1;
        } else {
            GEOFENCE_HILOGI(GEOFENCE_MODULE_SERVICE, "Not exit the geofence, the distance is not satisfied");
            return 0;
        }
    } else if (type_ == GeofenceDataUtils::GeofenceType::GEOFENCE_OPTION_ADD_POI_GEOFENCE) {
        distance = geofenceStateMachine_->GetDistance(locationData, cloudData_.center);
        if (distance >= cloudData_.radius * timesExitRadius_) {
            GEOFENCE_HILOGI(GEOFENCE_MODULE_SERVICE, "Exit the geofence");
            return 1;
        } else {
            GEOFENCE_HILOGI(GEOFENCE_MODULE_SERVICE, "Not exit the geofence, the distance is not satisfied");
            return 0;
        }
    } else {
        GEOFENCE_HILOGI(GEOFENCE_MODULE_SERVICE, "Exit, The status of the fence is not subscribed");
        return 0;
    }
    GEOFENCE_HILOGI(GEOFENCE_MODULE_SERVICE, "Exit");
    return 1;
}

int32_t GeofencePolicy::DistanceFrequency(std::vector<double> locationData)
{
    GEOFENCE_HILOGI(GEOFENCE_MODULE_SERVICE, "enter");
    double distance;
    if (type_ == GeofenceDataUtils::GeofenceType::GEOFENCE_OPTION_ADD_POINT_GEOFENCE) {
        distance = geofenceStateMachine_->GetDistance(locationData, pointOptions_.centerCoordinate) - pointOptions_.radius;
    } else if (type_ == GeofenceDataUtils::GeofenceType::GEOFENCE_OPTION_ADD_POI_GEOFENCE) {
        if (std::size(cloudData_.center)!= 0) {
            GEOFENCE_HILOGI(GEOFENCE_MODULE_SERVICE, "cloudData_.center[0] = %{public}lf", cloudData_.center[0]);
            GEOFENCE_HILOGI(GEOFENCE_MODULE_SERVICE, "cloudData_.center[1] = %{public}lf", cloudData_.center[1]);
            GEOFENCE_HILOGI(GEOFENCE_MODULE_SERVICE, "cloudData_.radius = %{public}lf", cloudData_.radius);
            distance = geofenceStateMachine_->GetDistance(locationData, cloudData_.center) - cloudData_.radius;
        } else {
            GEOFENCE_HILOGI(GEOFENCE_MODULE_SERVICE, "fencedata is not recived");
            return 0;
        }

    } else {
        GEOFENCE_HILOGI(GEOFENCE_MODULE_SERVICE, "Exit, The status of the fence is not subscribed");
        return 0;
    }
    GEOFENCE_HILOGI(GEOFENCE_MODULE_SERVICE, "distance = %{public}lf", distance);
    if (!FoolproofTacticsDistance(distance)) {
        return 0;
    }
    return 1;
    GEOFENCE_HILOGI(GEOFENCE_MODULE_SERVICE, "exit");
}

void GeofencePolicy::GeofenceState(std::vector<double> locationData)
{
    GEOFENCE_HILOGI(GEOFENCE_MODULE_SERVICE, "Enter");
    GEOFENCE_HILOGI(GEOFENCE_MODULE_SERVICE, "type = %{public}d", type_);
    int32_t stateFlag;
    GeofenceDataUtils::GeofenceData geofencecallbackData;
    if (!DistanceFrequency(locationData)) {
        return;
    }
    geofencecallbackData.type = type_;
    GEOFENCE_HILOGI(GEOFENCE_MODULE_SERVICE, "type_ = %{public}d", type_);
    GEOFENCE_HILOGI(GEOFENCE_MODULE_SERVICE, "stateCache_ = %{public}d", stateCache_);
    if (stateCache_ == -1) {
        stateFlag = GeofenceStateJudgement(locationData);
        geofencecallbackData.callbackvalue = stateFlag? GeofenceDataUtils::GeofenceValue::VALUE_INSIDE :
            GeofenceDataUtils::GeofenceValue::VALUE_OUTSIDE;
    } else {
        stateFlag = GeofenceStateJudgement(locationData);
        if (stateFlag != stateCache_ && stateFlag == GeofenceDataUtils::GeofenceValue::VALUE_INSIDE) {
            if (!GeofenceEnterDistanceJudgement(locationData)) {
                return;
            }
        } else if (stateFlag != stateCache_ && stateFlag == GeofenceDataUtils::GeofenceValue::VALUE_OUTSIDE) {
            if (!GeofenceExitDistanceJudgement(locationData)) {
                return;
            }
        } else if (stateFlag == stateCache_) {
            GEOFENCE_HILOGI(GEOFENCE_MODULE_SERVICE, "Geofence state is not changed");
            return;
        } else {
            return;
        }

        geofencecallbackData.callbackvalue = stateFlag? GeofenceDataUtils::GeofenceValue::VALUE_ENTER :
            GeofenceDataUtils::GeofenceValue::VALUE_EXIT;
    }
    JudgeGeofenceStateNotify(geofencecallbackData, stateFlag);
    GEOFENCE_HILOGI(GEOFENCE_MODULE_SERVICE, "exit");
    return;
}

void GeofencePolicy::JudgeGeofenceStateNotify(GeofenceDataUtils::GeofenceData &geofencecallbackData, int32_t stateFlag)
{
    GEOFENCE_HILOGI(GEOFENCE_MODULE_SERVICE, "enter");
    GEOFENCE_HILOGI(GEOFENCE_MODULE_SERVICE, "stateFlag = %{public}d", stateFlag);
    if (FoolproofTacticsTime()) {
        GEOFENCE_HILOGI(GEOFENCE_MODULE_SERVICE, "Notify begin");
        stateCache_ = stateFlag;
        GEOFENCE_HILOGI(GEOFENCE_MODULE_SERVICE, "stateCache_ = %{public}d", stateCache_);

        GEOFENCE_HILOGI(GEOFENCE_MODULE_SERVICE, "repeatEntryTimes_ = %{public}d", repeatEntryTimes_);
        if (repeatEntryTimes_ == 0) {
            NotifyGeofenceChange(geofencecallbackData);
        } else {
            if (GetTimeNowDay() == timeDay_) {
                if (entryTimes_ >= repeatEntryTimes_) {
                    GEOFENCE_HILOGI(GEOFENCE_MODULE_SERVICE,
                        "The number of repeated entries exceeds today, no message will be reported");
                    return;
                } else {
                    entryTimes_++;
                    NotifyGeofenceChange(geofencecallbackData);
                }
            } else {
                timeDay_ = GetTimeNowDay();
                entryTimes_ = 1;
                NotifyGeofenceChange(geofencecallbackData);
            }
        }
    } else {
        GEOFENCE_HILOGI(GEOFENCE_MODULE_SERVICE, "Foolproof strategy decides not to notify");
    }
    GEOFENCE_HILOGI(GEOFENCE_MODULE_SERVICE, "Exit");
}

void GeofencePolicy::NotifyGeofenceChange(const GeofenceDataUtils::GeofenceData& geofencecallbackData)
{
    GEOFENCE_HILOGI(GEOFENCE_MODULE_SERVICE, "Enter");

    // Call back for all listeners
    std::set<const sptr<IgeofenceCallback>, classcomp> listeners;
    bool isExists = false;
    for (auto it = listenerMap_.begin(); it != listenerMap_.end(); ++it) {
        if (it->first == geofencecallbackData.type) {
            isExists = true;
            listeners = (std::set<const sptr<IgeofenceCallback>, classcomp>)(it->second);
            break;
        }
    }
    if (!isExists) {
        GEOFENCE_HILOGI(GEOFENCE_MODULE_SERVICE, "No listener found for type: %{public}d", geofencecallbackData.type);
        GEOFENCE_HILOGI(GEOFENCE_MODULE_SERVICE, "Exit");
        return;
    }
    for (auto& listener : listeners) {
        listener->OnGeofenceChanged(geofencecallbackData);
    }

    GEOFENCE_HILOGI(GEOFENCE_MODULE_SERVICE, "Exit");
    return;
}

void GeofencePolicy::SubscribePoint(const GeofenceDataUtils::GeofenceType& type,
    const GeofenceDataUtils::PointOptions& pointOptions, const sptr<IgeofenceCallback>& callback)
{
    GEOFENCE_HILOGI(GEOFENCE_MODULE_SERVICE, "Enter");
    std::lock_guard lock(mutex_);
    GEOFENCE_RETURN_IF(callback == nullptr);
    auto object = callback->AsObject();
    GEOFENCE_RETURN_IF(object == nullptr);
    std::set<const sptr<IgeofenceCallback>, classcomp> listeners;
    bool isExists = false;
    for (auto it = listenerMap_.begin(); it != listenerMap_.end(); ++it) {
        if (it->first == type) {
            isExists = true;
            listeners = (std::set<const sptr<IgeofenceCallback>, classcomp>)(it->second);
            break;
        }
    }
    auto retIt = listeners.insert(callback);
    if (retIt.second) {
        object->AddDeathRecipient(geofenceCBDeathRecipient_);
    }

    if (!isExists) {
        std::pair<GeofenceDataUtils::GeofenceType, std::set<const sptr<IgeofenceCallback>, classcomp>> pair =
            std::make_pair(type, listeners);
        listenerMap_.insert(pair);
    }

    type_ = type;
    pointOptions_ = pointOptions;

    GEOFENCE_HILOGI(GEOFENCE_MODULE_SERVICE, "type_ = %{public}d, object = %{public}p, callback = %{public}p,\
        listeners.size = %{public}d, insertOk = %{public}d", type_, object.GetRefPtr(), callback.GetRefPtr(),\
        static_cast<unsigned int>(listeners.size()), retIt.second);
    GEOFENCE_HILOGI(GEOFENCE_MODULE_SERVICE, "Exit");
}

void GeofencePolicy::UnSubscribePoint(const GeofenceDataUtils::GeofenceType& type,
    const GeofenceDataUtils::PointOptions& pointOptions, const sptr<IgeofenceCallback>& callback)
{
    GEOFENCE_HILOGI(GEOFENCE_MODULE_SERVICE, "Enter");
    std::lock_guard lock(mutex_);
    GEOFENCE_RETURN_IF(callback == nullptr);
    auto object = callback->AsObject();
    GEOFENCE_RETURN_IF(object == nullptr);
    std::set<const sptr<IgeofenceCallback>, classcomp> listeners;
    bool isExists = false;
    for (auto it = listenerMap_.begin(); it != listenerMap_.end(); ++it) {
        if (it->first == type) {
            isExists = true;
            listeners = (std::set<const sptr<IgeofenceCallback>, classcomp>)(it->second);
            break;
        }
    }
    if (!isExists) {
        GEOFENCE_HILOGI(GEOFENCE_MODULE_SERVICE, "No listener found for type: %{public}d", type);
        GEOFENCE_HILOGI(GEOFENCE_MODULE_SERVICE, "Exit");
        return;
    }

    size_t eraseNum = listeners.erase(callback);
    if (eraseNum != 0) {
        object->RemoveDeathRecipient(geofenceCBDeathRecipient_);
    }

    GEOFENCE_HILOGI(GEOFENCE_MODULE_SERVICE, "type = %{public}d, object = %{public}p, callback = %{public}p,\
        listeners.size = %{public}d, eraseNum = %{public}zu", type, object.GetRefPtr(), callback.GetRefPtr(),\
        static_cast<unsigned int>(listeners.size()), eraseNum);

    type_ = GeofenceDataUtils::GeofenceType::GEOFENCE_OPTION_DEFAULT;
    stateCache_ = -1;
    judgementTimeFlag_ = 0;
    lastJudgementTimeFlag_ = 0;
    lastNotifyTimeFlag_ = 0;
    GEOFENCE_HILOGI(GEOFENCE_MODULE_SERVICE, "Exit");
}

void GeofencePolicy::GetCloudData(GeofenceDataUtils::CloudData& cloudData)
{
    GEOFENCE_HILOGI(GEOFENCE_MODULE_SERVICE, "enter");
    cloudData_ = cloudData;
    GEOFENCE_HILOGI(GEOFENCE_MODULE_SERVICE, "exit");
    return;
}

bool GeofencePolicy::CloudTileStrategy(const GeofenceDataUtils::PoiOptions& poiOptions)
{
    GEOFENCE_HILOGI(GEOFENCE_MODULE_SERVICE, "Enter");
    int32_t timeNowSec = GetTimeNow();
    if (timeNowSec >= TIME_TILING_STRATEGY_BEGIN && timeNowSec <= TIME_TILING_STRATEGY_END){
        GEOFENCE_HILOGI(GEOFENCE_MODULE_SERVICE, "Cloud Tile strategy is in effect");

        GEOFENCE_HILOGI(GEOFENCE_MODULE_SERVICE, "Get cloud cache data");
        GeofenceDataUtils::CloudData cloudData;
        cloudData = geofenceImplClient_->GetCloudCacheData(poiOptions);
        if (std::size(cloudData.fence) == 0) {
            GEOFENCE_HILOGE(GEOFENCE_MODULE_SERVICE, "GetCloudCacheData fail.");
            return false;
        }
        cloudData_ = cloudData;

        GEOFENCE_HILOGI(GEOFENCE_MODULE_SERVICE, "Tile strategy get new cloud data");
        srand((int)time(0));
        int32_t cloudTileStrategyTime = rand()%(TIME_TILING_STRATEGY_END - timeNowSec);
        int32_t timeNowSecCache =0;
        do {
            timeNowSecCache = GetTimeNow();
            GEOFENCE_HILOGI(GEOFENCE_MODULE_SERVICE, "rest of the time = %{public}d", timeNowSec + cloudTileStrategyTime -timeNowSecCache);
            sleep(5);
        } while (timeNowSecCache >= timeNowSec + cloudTileStrategyTime);

        GeofenceManager::CallbackCloudData callbackCloudData = std::bind(&GeofencePolicy::GetCloudData,
                this, std::placeholders::_1);
        if (!geofenceManager_->InitCloudDataCallback(geofenceImplClient_, poiOptions, callbackCloudData)) {
            GEOFENCE_HILOGE(GEOFENCE_MODULE_SERVICE, "%{public}s init msdp cloud data callback fail.", __func__);
            return false;
        }
    }else {
        GeofenceManager::CallbackCloudData callbackCloudData = std::bind(&GeofencePolicy::GetCloudData,
                this, std::placeholders::_1);
        if (!geofenceManager_->InitCloudDataCallback(geofenceImplClient_, poiOptions, callbackCloudData)) {
            GEOFENCE_HILOGE(GEOFENCE_MODULE_SERVICE, "%{public}s init msdp cloud data callback fail.", __func__);
            return false;
        }
    }
    GEOFENCE_HILOGI(GEOFENCE_MODULE_SERVICE, "Exit");
    return true;
}

void GeofencePolicy::SubscribePoi(const GeofenceDataUtils::GeofenceType& type,
    const GeofenceDataUtils::PoiOptions& poiOptions, const sptr<IgeofenceCallback>& callback)
{
    GEOFENCE_HILOGI(GEOFENCE_MODULE_SERVICE, "Enter");
    std::lock_guard lock(mutex_);
    GEOFENCE_RETURN_IF(callback == nullptr);
    auto object = callback->AsObject();
    GEOFENCE_RETURN_IF(object == nullptr);
    std::set<const sptr<IgeofenceCallback>, classcomp> listeners;
    bool isExists = false;
    for (auto it = listenerMap_.begin(); it != listenerMap_.end(); ++it) {
        if (it->first == type) {
            isExists = true;
            listeners = (std::set<const sptr<IgeofenceCallback>, classcomp>)(it->second);
            break;
        }
    }
    auto retIt = listeners.insert(callback);
    if (retIt.second) {
        object->AddDeathRecipient(geofenceCBDeathRecipient_);
    }

    if (!isExists) {
        std::pair<GeofenceDataUtils::GeofenceType, std::set<const sptr<IgeofenceCallback>, classcomp>> pair =
            std::make_pair(type, listeners);
        listenerMap_.insert(pair);
    }

    CloudTileStrategy(poiOptions);
    type_ = type;

    GEOFENCE_HILOGI(GEOFENCE_MODULE_SERVICE, "poiOptions.keyword: %{public}s, poiOptions.poiType: %{public}s, \
        poiOptions.poiCity: %{public}s", poiOptions.keyword.c_str(), poiOptions.poiType.c_str(),
        poiOptions.poiCity.c_str());
    GEOFENCE_HILOGI(GEOFENCE_MODULE_SERVICE, "type = %{public}d, object = %{public}p, callback = %{public}p,\
        listeners.size = %{public}d, insertOk = %{public}d", type, object.GetRefPtr(), callback.GetRefPtr(),\
        static_cast<unsigned int>(listeners.size()), retIt.second);
    GEOFENCE_HILOGI(GEOFENCE_MODULE_SERVICE, "Exit");
}

void GeofencePolicy::UnSubscribePoi(const GeofenceDataUtils::GeofenceType& type,
    const GeofenceDataUtils::PoiOptions& poiOptions, const sptr<IgeofenceCallback>& callback)
{
    GEOFENCE_HILOGI(GEOFENCE_MODULE_SERVICE, "Enter");
    std::lock_guard lock(mutex_);
    GEOFENCE_RETURN_IF(callback == nullptr);
    auto object = callback->AsObject();
    GEOFENCE_RETURN_IF(object == nullptr);
    std::set<const sptr<IgeofenceCallback>, classcomp> listeners;
    bool isExists = false;
    for (auto it = listenerMap_.begin(); it != listenerMap_.end(); ++it) {
        if (it->first == type) {
            isExists = true;
            listeners = (std::set<const sptr<IgeofenceCallback>, classcomp>)(it->second);
            break;
        }
    }
    if (!isExists) {
        GEOFENCE_HILOGI(GEOFENCE_MODULE_SERVICE, "No listener found for type: %{public}d", type);
        GEOFENCE_HILOGI(GEOFENCE_MODULE_SERVICE, "Exit");
        return;
    }

    size_t eraseNum = listeners.erase(callback);
    if (eraseNum != 0) {
        object->RemoveDeathRecipient(geofenceCBDeathRecipient_);
    }
    GEOFENCE_HILOGI(GEOFENCE_MODULE_SERVICE, "type = %{public}d, object = %{public}p, callback = %{public}p,\
        listeners.size = %{public}d, eraseNum = %{public}zu", type, object.GetRefPtr(), callback.GetRefPtr(),\
        static_cast<unsigned int>(listeners.size()), eraseNum);

    type_ = GeofenceDataUtils::GeofenceType::GEOFENCE_OPTION_DEFAULT;
    stateCache_ = -1;
    judgementTimeFlag_ = 0;
    lastJudgementTimeFlag_ = 0;
    lastNotifyTimeFlag_ = 0;

    GEOFENCE_HILOGI(GEOFENCE_MODULE_SERVICE, "Exit");
}

bool GeofencePolicy::SetFoolproofTactics(const GeofenceDataUtils::GeofenceType& type, double minuteTime)
{
    GEOFENCE_HILOGI(GEOFENCE_MODULE_SERVICE, "Enter");
    GEOFENCE_HILOGI(GEOFENCE_MODULE_SERVICE, "type = %{public}d", type);
    GEOFENCE_HILOGI(GEOFENCE_MODULE_SERVICE, "minuteTime = %{public}lf", minuteTime);
    int32_t timeNowSec = GetTimeNow();
    foolTacticsTime_ = timeNowSec + minuteTime * TIME_MIN_TO_SEC;
    GEOFENCE_HILOGI(GEOFENCE_MODULE_SERVICE, "foolTacticsState_ = %{public}d", foolTacticsTime_);
    GEOFENCE_HILOGI(GEOFENCE_MODULE_SERVICE, "Exit");
    return true;
}

int32_t GeofencePolicy::GetFoolproofTacticstime()
{
    GEOFENCE_HILOGI(GEOFENCE_MODULE_SERVICE, "foolTacticsTime_ = %{public}d", foolTacticsTime_);
    return foolTacticsTime_;
}

int32_t GeofencePolicy::GetStateCache()
{
    GEOFENCE_HILOGI(GEOFENCE_MODULE_SERVICE, "stateFlag_ = %{public}d", stateFlag_);
    return stateFlag_;
}
} // namespace Msdp
} // namespace OHOS
